package com.qunar.dyds.controller;

import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.joda.time.DateTime;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.HtmlUtils;

import com.qunar.base.meerkat.monitor.util.RequestUtil;
import com.qunar.base.meerkat.util.HashUtil;
import com.qunar.base.meerkat.util.StringUtil;
import com.qunar.dyds.Setting;
import com.qunar.dyds.dto.ListObject;
import com.qunar.dyds.entity.Competitor;
import com.qunar.dyds.entity.CompetitorStatus;
import com.qunar.dyds.entity.InviteCode;
import com.qunar.dyds.entity.Report;
import com.qunar.dyds.entity.ReportStatus;
import com.qunar.dyds.monitor.CompetitorMonitor;
import com.qunar.dyds.monitor.SystemMonitor;
import com.qunar.dyds.util.ImageUtil;
import com.qunar.dyds.util.ValidateUtil;
import com.qunar.recaptcha.client.ReCaptcha;
import com.qunar.recaptcha.client.ReCaptchaFactory;
import com.qunar.recaptcha.client.ReCaptchaResponse;
import com.qunar.upd.support.DiskFileItemFactorySupport;

@Controller("site")
public class SiteController extends AbstractController {

    @Resource
    protected DiskFileItemFactorySupport fileItemFactory;

    @RequestMapping(value = "home", method = RequestMethod.GET)
    public ModelAndView home() {
        return new ModelAndView("site/home", "data", competitorService.getHomeVO());
    }

    @RequestMapping(value = "competitor", method = RequestMethod.GET)
    public ModelAndView competitor(@RequestParam("link") String link) {

        logger.debug("competitor Start.");

        // 解密链接
        Integer id = Competitor.parseLink(link);
        if (id == null) {
            return errorView("未找到符合信息。");
        }

        // 查询选手信息
        Competitor c = competitorService.get(id);

        // 未找到数据|状态非法|链接无效
        if (c == null || !CompetitorStatus.ACTIVED.equals(c.getStatus()) || !c.getLink().equals(link)) {
            return errorView("未找到符合信息。");
        }

        logger.debug("competitor End.");

        return new ModelAndView("site/competitor", "item", c);
    }

    /**
     * 活动介绍
     */
    @RequestMapping(value = "intro", method = RequestMethod.GET)
    public String intro() {
        return "site/intro";
    }

    /**
     * 参赛条件
     */
    @RequestMapping(value = "term", method = RequestMethod.GET)
    public String term() {
        return "site/term";
    }

    /**
     * 报名入口
     */
    @RequestMapping(value = "sign", method = RequestMethod.GET)
    public String sign() {
        return "site/sign";
    }

    /**
     * 上传头像
     */
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "upload", method = RequestMethod.POST)
    public ModelAndView upload(HttpServletRequest request) {

        logger.debug("upload Start.");

        // 是否是上传请求
        if (!ServletFileUpload.isMultipartContent(request)) {
            logger.error("upload_not_multi_part, ip={}", RequestUtil.getIP(request));
            return new ModelAndView("site/upload", "json", errorJson("非法请求"));
        }

        // 防止恶意上传
        if (requestService.isSpam(request, "d_u_", Setting.UPLOAD_SPAM_LIMIT, Setting.UPLOAD_SPAM_EXPIRE)) {
            logger.error("upload_spam, ip={}", RequestUtil.getIP(request));
            SystemMonitor.UPLOAD_SPAM.increment();
            return new ModelAndView("site/upload", "json", errorJson("您的操作过于频繁，请休息一下"));
        }

        ServletFileUpload upload = new ServletFileUpload(fileItemFactory);
        upload.setFileSizeMax(Setting.UPLOAD_MAX_SIZE);
        upload.setSizeMax(Setting.UPLOAD_MAX_SIZE);

        FileItem fileItem = null;
        try {
            List<FileItem> items = upload.parseRequest(request);
            for (FileItem item : items) {
                if (!item.isFormField() && !StringUtil.isEmpty(item.getName())) {
                    fileItem = item;
                    break;
                }
            }
        } catch (FileUploadException e) {
            logger.error("upload_parse_excetion, e={}, ip={}", e, RequestUtil.getIP(request));
            return new ModelAndView("site/upload", "json", errorJson("文件过大"));
        }

        if (fileItem == null) {
            return new ModelAndView("site/upload", "json", errorJson("请选择文件"));
        }

        try {
            if (!"jpg".equals(ImageUtil.getImageType(fileItem.getInputStream()))) {
                return new ModelAndView("site/upload", "json", errorJson("请选择jpg, jpeg格式文件"));
            }
        } catch (Exception e) {
            logger.error("upload_invaild_type, e={}, ip={}", e, RequestUtil.getIP(request));
            return new ModelAndView("site/upload", "json", errorJson("请选择jpg, jpeg格式文件"));
        }

        String filename = System.currentTimeMillis() + ".jpg";
        File file = new File(Setting.AVATAR_FOLDER, filename);
        try {
            fileItem.write(file);
        } catch (Exception e) {
            logger.error("upload_write_failed, e={}, ip={}", e, RequestUtil.getIP(request));
            return new ModelAndView("site/upload", "json", errorJson("文件写入失败"));
        }

        logger.debug("upload End.");

        return new ModelAndView("site/upload", "json", json(STATUS_OK, filename));
    }

    /**
     * 提交注册
     */
    @ResponseBody
    @RequestMapping(value = "sign", method = RequestMethod.POST)
    public Map<Object, Object> signSubmit(HttpServletRequest request) {

        logger.debug("signSubmit Start.");

        String code = HtmlUtils.htmlEscape(request.getParameter("code"));
        String name = HtmlUtils.htmlEscape(request.getParameter("name"));
        String certNo = HtmlUtils.htmlEscape(request.getParameter("certNo"));
        String school = HtmlUtils.htmlEscape(request.getParameter("school"));
        String major = HtmlUtils.htmlEscape(request.getParameter("major"));
        String grade = HtmlUtils.htmlEscape(request.getParameter("grade"));
        String mobile = HtmlUtils.htmlEscape(request.getParameter("mobile"));
        String email = HtmlUtils.htmlEscape(request.getParameter("email"));
        String username = HtmlUtils.htmlEscape(request.getParameter("username"));
        String password = request.getParameter("password");
        String repassword = request.getParameter("repassword");
        String comment = HtmlUtils.htmlEscape(request.getParameter("comment"));
        String avatar = HtmlUtils.htmlEscape(request.getParameter("avatar"));

        if (StringUtil.isEmpty(code)) {
            return errorJson("报名序号不能为空");
        }
        if (!PATTERN_CODE.matcher(code).matches()) {
            return errorJson("请填写正确的报名序号");
        }
        if (StringUtil.isEmpty(name)) {
            return errorJson("姓名不能为空");
        }
        if (name.length() > 8) {
            return errorJson("姓名不能超过8个字符");
        }
        if (StringUtil.isEmpty(certNo)) {
            return errorJson("身份证不能为空");
        }
        if (ValidateUtil.isValidCart(certNo) != null) {
            return errorJson("请填写正确的身份证号");
        }
        if (StringUtil.isEmpty(school)) {
            return errorJson("学校不能为空");
        }
        if (school.length() > 50) {
            return errorJson("学校不能超过50个字符");
        }
        if (StringUtil.isEmpty(major)) {
            return errorJson("专业不能为空");
        }
        if (school.length() > 50) {
            return errorJson("专业不能超过50个字符");
        }
        if (StringUtil.isEmpty(grade)) {
            return errorJson("年级不能为空");
        }
        if (school.length() > 50) {
            return errorJson("年级不能超过50个字符");
        }
        if (StringUtil.isEmpty(mobile)) {
            return errorJson("手机号码不能为空");
        }
        if (!ValidateUtil.isMobile(mobile)) {
            return errorJson("请填写正确的手机号码");
        }
        if (StringUtil.isEmpty(email)) {
            return errorJson("邮箱不能为空");
        }
        if (school.length() > 50) {
            return errorJson("邮箱不能超过50个字符");
        }
        if (!ValidateUtil.isEmail(email)) {
            return errorJson("请填写正确的邮箱");
        }
        if (StringUtil.isEmpty(username)) {
            return errorJson("用户名不能为空");
        }
        if (!PATTERN_USERNAME.matcher(username).matches()) {
            return errorJson("用户名必须为2至16位的英文和数字");
        }
        if (StringUtil.isEmpty(password)) {
            return errorJson("密码不能为空");
        }
        if (password.length() < 4 || password.length() > 30) {
            return errorJson("请使用4至30位字符作为密码");
        }
        if (!password.equals(repassword)) {
            return errorJson("密码不一致");
        }
        if (StringUtil.isEmpty(avatar) || !avatar.startsWith(Setting.AVATAR_PREFIX)) {
            return errorJson("请上传头像");
        }
        if (school.length() > 512) {
            return errorJson("头像地址不能超过512个字符");
        }

        Competitor c = new Competitor();
        c.setInviteCode(code);
        c.setName(name);
        c.setSchool(school);
        c.setMajor(major);
        c.setGrade(grade);
        c.setMobile(mobile);
        c.setEmail(email);
        c.setUsername(username);
        c.setPassword(StringUtil.toHexString(HashUtil.hashMD5(password)));
        c.setComment(comment);
        c.setAvatar(avatar);
        c.setAge(getAge(certNo));
        c.setVoteCount(0);
        c.setStatus(CompetitorStatus.LOCKED);
        c.setCreated(new Date());

        InviteCode pCode = new InviteCode();
        pCode.setCertNo(certNo);

        try {
            competitorService.add(c, pCode);
        } catch (DuplicateKeyException e) {
            logger.error("sign_duplicate_key, e={}, ip={}", e, RequestUtil.getIP(request));
            SystemMonitor.SIGN_FAIL.increment();
            return errorJson("注册失败，报名序号或用户名已被占用。");
        } catch (IllegalArgumentException e) {
            logger.error("sign_invalid_argument, e={}, ip={}", e, RequestUtil.getIP(request));
            SystemMonitor.SIGN_FAIL.increment();
            return errorJson("注册失败，" + e.getMessage());
        } catch (Exception e) {
            logger.error("sign_failed, e={}, ip={}", e, RequestUtil.getIP(request));
            SystemMonitor.SIGN_FAIL.increment();
            return errorJson("注册失败。");
        }

        logger.debug("signSubmit End.");

        return dataJson("注册成功");
    }

    /**
     * 投诉举报
     */
    @RequestMapping(value = "report", method = RequestMethod.GET)
    public String report() {
        return "site/report";
    }

    /**
     * 提交投诉信息
     */
    @ResponseBody
    @RequestMapping(value = "report", method = RequestMethod.POST)
    public Map<Object, Object> reportSubmit(HttpServletRequest request) {

        logger.debug("reportSubmit Start.");

        String code = HtmlUtils.htmlEscape(request.getParameter("code"));

        if (requestService.isSpam(request, "d_r_", Setting.REPORT_SPAM_LIMIT, Setting.REPORT_SPAM_EXPIRE)) {
            logger.error("report_spam, code={}, ip={}", code, RequestUtil.getIP(request));
            CompetitorMonitor.REPORT_SPAM.increment(code);
            return errorJson("您的操作过于频繁，请休息一下");
        }

        // 获取参数
        String name = HtmlUtils.htmlEscape(request.getParameter("name"));
        String mobile = HtmlUtils.htmlEscape(request.getParameter("mobile"));
        String cname = HtmlUtils.htmlEscape(request.getParameter("cname"));
        String content = HtmlUtils.htmlEscape(request.getParameter("content"));

        // 参数检查
        if (StringUtil.isEmpty(name)) {
            return errorJson("请填写你的姓名。");
        }
        if (name.length() > 8) {
            return errorJson("姓名不能超过8个字符");
        }
        // 验证电话号码
        if (StringUtil.isEmpty(mobile)) {
            return errorJson("请填写你的电话号码，以便我们能和你联系。");
        }
        if (!ValidateUtil.isMobile(mobile)) {
            return errorJson("请填写正确的电话号码。");
        }
        if (StringUtil.isEmpty(code)) {
            return errorJson("请填写被投诉人报名序号。");
        }
        if (!PATTERN_CODE.matcher(code).matches()) {
            return errorJson("请填写正确的报名序号");
        }
        if (StringUtil.isEmpty(content)) {
            return errorJson("投诉内容不能为空");
        }
        if (content.length() > 2000) {
            return errorJson("投诉内容不能超过2000个字符");
        }

        // 查询报名序列号是否存在
        InviteCode inviteCode = inviteCodeService.get(code);
        if (inviteCode == null) {
            return errorJson("无效的报名序号");
        }

        // 参数绑定
        Report report = new Report();

        report.setName(name);
        report.setMobile(mobile);
        report.setContent(content);
        report.setInviteCode(code);
        report.setUsername(cname);
        report.setStatus(ReportStatus.CREATED);
        report.setCreated(new Date());

        // 保存投诉
        try {
            reportService.add(report);
        } catch (Exception e) {
            logger.error("report_failed, e={}, ip={}", e, RequestUtil.getIP(request));
            SystemMonitor.REPORT_FAIL.increment();
            return errorJson("投诉异常");
        }

        logger.debug("reportSubmit End.");

        return dataJson("你的投诉已受理");
    }

    /**
     * 显示投票
     * 
     * @param request
     * @return
     */
    @RequestMapping(value = "vote", method = RequestMethod.GET)
    public ModelAndView vote(@RequestParam(value = "name", required = false) String name,
            @RequestParam(value = "startIndex", required = false, defaultValue = "0") int startIndex) {

        logger.debug("vote Start.");

        name = HtmlUtils.htmlEscape(name);

        // TODO 查询条件判定
        Map<String, Object> cond = new HashMap<String, Object>();
        cond.put("status", CompetitorStatus.ACTIVED);
        if (!StringUtil.isEmpty(name)) {
            if (name.startsWith("DYDS_")) {
                cond.put("inviteCode", name);
            } else {
                // name
                cond.put("name", name);
            }
        }

        // 固定页数据为12条
        int row = 12;

        // 总列表量
        int totalCount = competitorService.getCount(cond);

        // 按得票数排序
        cond.put("sort", "vote_count desc");
        List<Competitor> beans = competitorService.search(cond, startIndex, row);

        ListObject lstObj = new ListObject(startIndex, row, totalCount, beans);

        ModelAndView view = new ModelAndView("site/vote");
        view.addObject("lstObj", lstObj);
        view.addObject("name", name);

        logger.debug("vote End.");

        return view;
    }

    /**
     * 投票
     * 
     * @param link
     * @param request
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "vote", method = RequestMethod.POST)
    public Map<Object, Object> voteSubmit(@RequestParam("link") String link,
            @RequestParam("captcha") String captcha, HttpServletRequest request) {

        logger.debug("voteSubmit Start.");

        long now = System.currentTimeMillis();
        long start = Setting.VOTE_START_TIME.getTime();
        long end = Setting.VOTE_END_TIME.getTime();
        if (now < start) {
            return errorJson("投票未开始。");
        }
        if (now > end) {
            return errorJson("投票已结束。");
        }

        // 解密链接
        Integer id = Competitor.parseLink(link);
        if (id == null) {
            return errorJson("没有找到选手信息。");
        }

        // 验证码 2012-04-10
        ReCaptcha reCaptcha = ReCaptchaFactory.newReCaptcha("pGhQcieD");
        ReCaptchaResponse response = reCaptcha.checkAnswer(request, captcha);
        if (!response.isValid()) {
            return errorJson("您输入的验证码不正确，请重新输入！");
        }

        // 防止刷票, 针对单个人
        if (requestService.isSpam(request, "d_v_" + id, Setting.VOTE_SPAM_LIMIT, Setting.VOTE_SPAM_EXPIRE)) {
            logger.error("vote_spam, id={}, ip={}", id, RequestUtil.getIP(request));
            CompetitorMonitor.VOTE_SPAM.increment(id.toString());
            return errorJson("您的操作过于频繁，请休息一下");
        }

        int cnt = 0;

        try {
            cnt = competitorService.vote(id, CompetitorStatus.ACTIVED);
        } catch (Exception e) {
            logger.error("vote_failed, id={}, e={}, ip={}", e, RequestUtil.getIP(request));
            SystemMonitor.VOTE_FAIL.increment();
            return errorJson("投票失败");
        }

        if (cnt == 0) {
            return errorJson("没有找到选手信息。");
        } else {
            logger.info("vote_ok, id={},ip={}", id, RequestUtil.getIP(request));
        }

        logger.debug("voteSubmit End.");

        return dataJson(link);
    }

    @RequestMapping(value = "contact", method = RequestMethod.GET)
    public String contact() {
        return "site/contact";
    }

    /**
     * @param username
     * @param password
     * @param session
     * @return
     */
    @ResponseBody
    @RequestMapping(value="login", method = RequestMethod.POST)
    public Map<Object, Object> login(@RequestParam("username") String username,
            @RequestParam("password") String password, HttpSession session, HttpServletRequest request) {

        logger.debug("login Start.");

        if (StringUtil.isEmpty(username)) {
            return errorJson("请填写登陆用户名");
        }
        if (StringUtil.isEmpty(password)) {
            return errorJson("请填写登陆密码");
        }

        Competitor c = competitorService.get(username);
        if (c == null) {
            logger.error("login_failed, non exist. username={}, ip={}", username, RequestUtil.getIP(request));
            SystemMonitor.LOGIN_FAIL.increment();
            return errorJson("用户不存在");
        }
        if (CompetitorStatus.DELETED.equals(c.getStatus())) {
            logger.error("login_failed, deleted, username={}, ip={}", username, RequestUtil.getIP(request));
            SystemMonitor.LOGIN_FAIL.increment();
            return errorJson("用户已被删除");
        }
        String expectedPass = StringUtil.toHexString(HashUtil.hashMD5(password));
        if (!c.getPassword().equals(expectedPass)) {
            logger.error("login_failed, invalid password. username={}, password={}, ip={}", new Object[]{ username, password, RequestUtil.getIP(request)});
            SystemMonitor.LOGIN_FAIL.increment();
            return errorJson("密码错误");
        }

        session.setAttribute(Setting.SESSION_KEY, c);

        logger.debug("login End.");

        return dataJson("登陆成功");
    }

    @RequestMapping(value = "logout", method = RequestMethod.GET)
    public String logout(HttpSession session) {

        logger.debug("logout Start.");

        session.removeAttribute(Setting.SESSION_KEY);
        session.invalidate();

        logger.debug("logout End.");

        return "redirect:/site/home.htm";
    }

    /**
     * 查看个人信息
     * 
     * @param session
     * @return
     */
    @RequestMapping(value = "profile", method = RequestMethod.GET)
    public ModelAndView profile(HttpSession session) {

        logger.debug("profile Start.");

        Competitor c = (Competitor) session.getAttribute(Setting.SESSION_KEY);
        if (c == null) {
            return errorView("未登陆");
        }

        logger.debug("profile End.");

        return new ModelAndView("site/profile", "item", c);
    }

    @RequestMapping(value = "edit", method = RequestMethod.GET)
    public ModelAndView edit(HttpSession session) {

        logger.debug("edit Start.");

        Competitor c = (Competitor) session.getAttribute(Setting.SESSION_KEY);
        if (c == null) {
            return errorView("未登陆");
        }

        logger.debug("edit End.");

        return new ModelAndView("site/edit", "item", c);
    }

    /**
     * 修改信息
     */
    @ResponseBody
    @RequestMapping(value = "edit", method = RequestMethod.POST)
    public Map<Object, Object> edit(HttpServletRequest request, HttpSession session) {
        
        logger.debug("editSubmit End.");

        Competitor sc = (Competitor) session.getAttribute(Setting.SESSION_KEY);
        if (sc == null) {
            return errorJson("未登陆");
        }

        String mobile = HtmlUtils.htmlEscape(request.getParameter("mobile"));
        String email = HtmlUtils.htmlEscape(request.getParameter("email"));
        String password = request.getParameter("password");
        String repassword = request.getParameter("repassword");
        String comment = HtmlUtils.htmlEscape(request.getParameter("comment"));
        String avatar = HtmlUtils.htmlEscape(request.getParameter("avatar"));

        if (StringUtil.isEmpty(mobile)) {
            return errorJson("手机号码不能为空");
        }
        if (!ValidateUtil.isMobile(mobile)) {
            return errorJson("请填写正确的手机号码");
        }
        if (StringUtil.isEmpty(email)) {
            return errorJson("邮箱不能为空");
        }
        if (!ValidateUtil.isEmail(email)) {
            return errorJson("请填写正确的邮箱");
        }
        if (StringUtil.isEmpty(avatar) || !avatar.startsWith(Setting.AVATAR_PREFIX)) {
            return errorJson("请上传头像");
        }

        Competitor c = new Competitor();
        if (!StringUtil.isEmpty(password)) {
            if (password.length() < 4 || password.length() > 30) {
                return errorJson("请使用4至30位字符作为密码");
            }
            // 密码不为空时，修改密码
            if (!password.equals(repassword)) {
                return errorJson("两次输入密码不一致");
            }
            password = StringUtil.toHexString(HashUtil.hashMD5(password));
            c.setPassword(password);
        }

        c.setId(sc.getId());
        c.setMobile(mobile);
        c.setEmail(email);
        c.setComment(comment);
        c.setAvatar(avatar);
        c.setStatus(CompetitorStatus.LOCKED);
        c.setUpdated(new Date());

        try {
            Competitor competitor = competitorService.get(sc.getId());
            if (competitor == null) {
                return errorJson("用户不存在。");
            }
            if (CompetitorStatus.DELETED.equals(competitor.getStatus())) {
                return errorJson("用户已被删除。");
            }

            competitorService.update(c);

            sc.setMobile(c.getMobile());
            sc.setEmail(c.getEmail());
            sc.setPassword(c.getPassword());
            sc.setComment(c.getComment());
            sc.setAvatar(c.getAvatar());
        } catch (Exception e) {
            logger.error("edit_failed, e={}, ip={}", e, RequestUtil.getIP(request));
            SystemMonitor.EDIT_FAIL.increment();
            return errorJson("修改信息失败。");
        }

        logger.debug("editSubmit End.");

        return dataJson("修改成功");
    }

    private int getAge(String certNo) {
        DateTime c = new DateTime();
        int year = c.getYear();
        int month = c.getMonthOfYear();

        int cyear = Integer.valueOf(certNo.substring(6, 10));
        int cm = Integer.valueOf(certNo.substring(10, 12));

        int age = year - cyear;
        return (month < cm) ? age - 1 : age;
    }
}
